package com.mspbots.core.script.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.core.AbstractBaseServiceImpl;
import com.mspbots.core.script.mapper.SystemScriptMapper;
import com.mspbots.core.script.model.ScriptParam;
import com.mspbots.core.script.model.SystemScript;
import com.mspbots.core.script.model.TenantScript;
import com.mspbots.core.script.service.SystemScriptService;
import com.mspbots.core.script.service.TenantScriptService;
import com.mspbots.core.wise.mapper.TenantMapper;
import com.mspbots.core.wise.model.Tenant;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * com.mspbots.core.script.service.impl.SystemScriptImpl
 *
 * @author Jason
 * @date 2019/12/16
 **/
@Log4j2
@Service
public class SystemScriptImpl extends AbstractBaseServiceImpl<SystemScriptMapper, SystemScript> implements SystemScriptService {

    @Autowired
    private TenantMapper tenantMapper;
    @Autowired
    private TenantScriptService tenantScriptService;

    @Override
    public SystemScript saveOrUpdateScript(SystemScript entity) {
        //deal with duplicate save!
        if (null == entity.getId()) {
            Map<String, Object> columnMap = new HashMap<>();
            columnMap.put("name", entity.getName());
            columnMap.put("scope", entity.getScope());
            columnMap.put("trigger_event", entity.getScope());
            List<SystemScript> list = this.listByMap(columnMap);
            if (!CollectionUtils.isEmpty(list)) {
                return list.get(0);
            }
        }

        if (entity.getScope().toLowerCase().equals("system")) {
            entity.setApplyTenant(false);
        }
        if (StringUtils.isEmpty(entity.getVersion()) || entity.getVersion().equals("1.0")) {
            LocalDate date = LocalDate.now();
            String dateStr = date.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            String versionStr = "VS." + dateStr + ".001";
            entity.setVersion(versionStr);
        } else if (entity.getVersion().startsWith("VS.")) {
            entity.setVersion(addVersionStr(entity.getVersion()));
        } else {
            LocalDate date = LocalDate.now();
            String dateStr = date.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
            String versionStr = "VS." + dateStr + ".001";
            entity.setVersion(versionStr);
        }
        saveOrUpdate(entity);
        if (entity.getApplyTenant()) {
            List<Tenant> tenants = tenantMapper.selectList(Wrappers.emptyWrapper());

            tenants.parallelStream().forEach(tenant -> {
                TenantScript tenantScript = tenantScriptService.getOne(
                        Wrappers.<TenantScript>lambdaQuery()
                                .eq(TenantScript::getSystemScriptId, entity.getId())
                                .eq(TenantScript::getTenantId, tenant.getId())
                );
                if (tenantScript == null) {
                    tenantScript = new TenantScript();
                    BeanUtils.copyProperties(entity, tenantScript, "id", "create_time");
                    tenantScript.setTenantId(tenant.getId());
                    tenantScript.setSystemScriptId(entity.getId());
                    tenantScriptService.save(tenantScript);
                } else if (StringUtils.isEmpty(tenantScript.getVersion()) || tenantScript.getVersion().startsWith("VS.") || tenantScript.getVersion().equals("1.0")) {
                    BeanUtils.copyProperties(entity, tenantScript, "id");
                    tenantScriptService.updateById(tenantScript);
                } else if (tenantScript.getVersion().startsWith("VT.")) {
                    BeanUtils.copyProperties(entity, tenantScript, "id", "paramsValue", "version",
                            "description", "updateTime", "applyTenant", "language", "enable", "createTime", "triggerEvent", "scope");
                    tenantScriptService.updateById(tenantScript);
                } else {
                    BeanUtils.copyProperties(entity, tenantScript, "id");
                    tenantScriptService.updateById(tenantScript);
                }

                log.info("system:tenantId::" + tenantScript.getTenantId() + "::version :: " + tenantScript.getVersion());

            });
        }
        return entity;
    }

    @Override
    public Boolean addParamByName(ScriptParam queryParams) {
        QueryWrapper<SystemScript> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("name", queryParams.getScriptName());
        SystemScript systemScript = this.baseMapper.selectOne(queryWrapper);
        String paramJsonStr = parseParamJson(queryParams);
        SystemScript paramScript = dealParams(paramJsonStr, systemScript.getParamsJson(), systemScript.getParamsValue());
        systemScript.setParamsJson(paramScript.getParamsJson());
        systemScript.setParamsValue(paramScript.getParamsValue());
        this.updateById(systemScript);

        QueryWrapper<TenantScript> tenantWrapper = new QueryWrapper<>();
        tenantWrapper.eq("system_script_id", systemScript.getId());
        List<TenantScript> list = this.tenantScriptService.list(tenantWrapper);
        list.stream().forEach(t -> {
            SystemScript param = dealParams(paramJsonStr, t.getParamsJson(), t.getParamsValue());
            t.setParamsJson(param.getParamsJson());
            t.setParamsValue(param.getParamsValue());
        });
        this.tenantScriptService.updateBatchById(list);

        return true;
    }

    @Override
    public List<SystemScript> querylist(QueryWrapper<SystemScript> queryWrapper) {
        List<SystemScript> list = this.list(queryWrapper);
        list.sort(Comparator.comparing(SystemScript::getName));
        return list;
    }

    private String parseParamJson(ScriptParam queryParams) {
        ObjectNode paramsJsonObj = this.objectMapper.createObjectNode();
        paramsJsonObj.put("text", queryParams.getText());
        paramsJsonObj.put("label", queryParams.getLabel());
        if (!ObjectUtils.isEmpty(queryParams.getHint())) {
            paramsJsonObj.put("hint", queryParams.getHint());
        }
        paramsJsonObj.put("type", queryParams.getType());
        if (queryParams.getType().equalsIgnoreCase("switch")) {
            if (queryParams.getDefaultValue().equalsIgnoreCase("false")) {
                paramsJsonObj.put("defaultValue", false);
            } else {
                paramsJsonObj.put("defaultValue", true);
            }
        } else {
            paramsJsonObj.put("defaultValue", queryParams.getDefaultValue());
        }
        return paramsJsonObj.toString();
    }

    private SystemScript dealParams(String paramJsonStr, String paramsJson, JsonNode paramsValue) {
        SystemScript resultParam = new SystemScript();
        if (ObjectUtils.isEmpty(paramsJson)) {
            ObjectNode paramsJsonObj = this.objectMapper.createObjectNode();
            ArrayNode paramsJsonArr = this.objectMapper.createArrayNode();
            JsonNode param = this.objectMapper.convertValue(paramJsonStr, JsonNode.class);
            paramsJsonArr.add(param);
            paramsJsonObj.set("fields", paramsJsonArr);
            resultParam.setParamsJson(paramsJsonObj.toString());
        } else {

            ObjectNode paramsJsonObj = null;
            try {
                paramsJsonObj = this.objectMapper.readValue(paramsJson, ObjectNode.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            JsonNode param = null;
            try {
                param = this.objectMapper.readValue(paramJsonStr, JsonNode.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            ArrayNode fieldsArr = paramsJsonObj.get("fields").deepCopy();
            int index = -1;
            for (int i = 0; i < fieldsArr.size(); i++) {
                if (fieldsArr.get(i).get("text").asText().equalsIgnoreCase(param.get("text").asText())) {
                    index = i;
                    break;
                }
            }
            if (index >= 0) {
                fieldsArr.remove(index);
            }

            fieldsArr.add(param);

            paramsJsonObj.set("fields", fieldsArr);
            resultParam.setParamsJson(paramsJsonObj.toString());
        }


        JsonNode param = null;
        try {
            param = this.objectMapper.readValue(paramJsonStr, JsonNode.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        String text = param.get("text").asText();

        ObjectNode paramsValueObj = null;
        try {
            paramsValueObj = this.objectMapper.readValue(paramsValue.toString(), ObjectNode.class).deepCopy();
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        if (param.get("type").asText().equalsIgnoreCase("switch")) {
            paramsValueObj.put(text, param.get("defaultValue").asBoolean());
        } else {
            paramsValueObj.put(text, param.get("defaultValue").asText());
        }

        JsonNode paramsValueJson = null;
        try {
            paramsValueJson = this.objectMapper.readValue(paramsValueObj.toString(), JsonNode.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        resultParam.setParamsValue(paramsValueJson);

        return resultParam;
    }


    private String addVersionStr(String oldVersion) {
        String numStr = oldVersion.substring(oldVersion.lastIndexOf(".") + 1, oldVersion.length());
        String index = Integer.parseInt(numStr) + 1 + "";
        String indexString = "";
        if (index.length() < 4) {
            for (int i = 0; i < 4 - index.length(); i++) {
                index = "0" + index;
            }
        }
        String versionStr = oldVersion.substring(0, oldVersion.lastIndexOf(".") + 1) + index;

        return versionStr;
    }
}
